// vue3 响应式原理： 利用Proxy对象对数据拦截
function isObj(obj) {
    return typeof obj === 'object'
}

function hasOwn(obj, key) {
    return obj.hasOwnProperty(key)
}
// 
const toProxy = new WeakMap() //obj: observe   // 缓存对象
const toRaw = new WeakMap() // observe:obj  // 缓存代理后的对象

function reactive(obj) {
    if (!isObj(obj)) {
        return obj
    }
    // 查找传入的对象是否已经代理过，  有则直接返回这个代理过后的代理对象
    if (toProxy.has(obj)) {
        return toProxy.get(obj)
    }
    // 传入的就是代理对象，则直接返回，不用反复代理
    if (toRaw.has(obj)) {
        return obj
    }
    const observe = new Proxy(obj, {
        get(target, key, receiver) {
            // 访问
            const res = Reflect.get(target, key, receiver)
            console.log(`获取${key}:${res}`)
                // 依赖收集
            track(target, key)
                // 运行时的嵌套递归
            return isObj(res) ? reactive(obj) : res
        },
        set(target, key, value, receiver) {
            const hadkey = hasOwn(target, key)
            const oldVal = target[key]
            const res = Reflect.set(target, key, value, receiver)
            if (!hadkey) {
                console.log(`新增${key}：${value}`)
                trigger(target, 'ADD', key)
            } else if (oldVal !== value) {
                console.log(`设置${key}：${value}`)
                trigger(target, 'SET', key)
            }
            return res
        },
        deleteProperty(target, key) {
            const hadkey = hasOwn(target, key)
            const res = Reflect.deleteProperty(target, key)
                // 
            if (hadkey && res) {
                console.log(`删除${key}`)
                trigger(target, 'DELETE', key)
            }
            return res
        }
    })

    // 缓存
    toProxy.set(obj, observe)
    toRaw.set(observe, obj)
    return observe
}
// 保存依赖回调函数
const activeReativeEffectStack = []
    // 依赖收集执行
    // 基本结构 {target,{key:[eff1,eff2]}}
let targetMap = new WeakMap() // 保存依赖收集
    // 跟踪收集
function track(target, key) {
    // 从栈中获取响应函数
    const effect = activeReativeEffectStack[activeReativeEffectStack.length - 1]
    if (effect) {
        let depsMap = targetMap.get(target)
        if (!depsMap) {
            // 首次访问target
            depsMap = new Map()
            targetMap.set(target, depsMap)
        }
        // 存放key  
        let deps = depsMap.get(key)
        if (!deps) {
            deps = new Set()
            depsMap.set(key, deps)
        }
        if (!deps.has(effect)) {
            deps.add(effect)
        }
    }
}
// 触发依赖  触发target.key 对应的响应函数
function trigger(target, type, key) {
    // 获取依赖表
    const depsMap = targetMap.get(target)
    if (depsMap) {
        // 获取响应函数集合
        const deps = depsMap.get(key)
        const effects = new Set()
        if (deps) {
            deps.forEach(effect => {
                effects.add(effect)
            });
        }
        // 数组 新增或删除
        if (type === "ADD" || type === "DELETE") {
            if (Array.isArray(target)) {
                const deps = depsMap.get('length')
                if (deps) {
                    deps.forEach(effect => {
                        deps.add(effect)
                    });
                }
            }
        }
        //
        effects.forEach(effect => effect())
    }

}



// 监听数据更新
function effect(fn) {
    // 1. 异常处理
    // 2. 执行函数
    // 3. 放置到 activeReativeEffectStack
    const rxEffect = function(...args) {
        try {
            activeReativeEffectStack.push(rxEffect)
            return fn(...args)
        } finally {
            activeReativeEffectStack.pop()

        }
    }
    rxEffect() // 默认立即执行
    return rxEffect
}

const data = {
    foo: "fooooo",
    bar: {
        a: 1
    }
}
const react = reactive(data)

// 1. 获取
// react.foo
// 2.  设置已存在的属性
// react.foo = "fooooooo"
// 3. 设置不存在的属性
// react.baz = "bazzz"
// 4. 设置嵌套对象
// react.bar.a = 10



// 避免重复代理的问题  
//  console.log(reactive(data) === react)

effect(() => {
    console.log("发生了变化", react.foo)
    console.log("发生了变化", react.a)
})
react.foo = "f0ff01f0f01f"
react.a = "32222"
    // setInterval(() => {
    //     react.foo += "==4"
    // }, 1500);




// 问题分析  为什么要改用proxy
//1. difineProperty 只能监听对象的个某个属性，不能监听全对象。
//2. proxy可以监听全对象，故省去对象遍历监听，提升性能
//3. proxy可以监听数组，不用单独对数组做特异性操作